﻿Imports System.ServiceModel

Module MPQClient
    Public Server As Server.ServerClient
    Friend Username As String
    Friend EndpointAdr As ServiceModel.EndpointAddress

    Friend SingleGameOnly As Boolean
    Friend FirstPlay As Boolean = False

    Friend ServerPassword As String = ""
    Friend ServerNeedPassword As Boolean = False

    ''' <CommandLineArgs>
    ''' /f - First Play - Show Tutorial
    ''' /s %SERVER% - Server Name - Automatically Selects this Server
    ''' /u %USER% - Username - Auto login with this Username (Creates it if the user doesn't exist)
    ''' /p %PASS% - Password - Defines password for auto login
    '''
    ''' /DEBUG %GAME% - Enables debug mode (autoplay) %GAME% defines the game to create/connect to.
    ''' </CommandLineArgs>

    Sub Main()
        'If Not IO.File.Exists("MPQClient.exe.config") Then
        '    Dim FS As New IO.FileStream("MPQClient.exe.config", IO.FileMode.Create)
        '    With Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("MPQClient.app.config")
        '        Dim Buffer(.Length) As Byte
        '        .Read(Buffer, 0, .Length)
        '        FS.Write(Buffer, 0, .Length)
        '        FS.Close()
        '    End With
        '    IO.File.SetAttributes("MPQClient.exe.config", IO.FileAttributes.Hidden)
        '    FirstPlay = True
        'End If

        'AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "MPQClient.exe.config")

        Application.EnableVisualStyles()

        If My.Application.CommandLineArgs.Contains("/f") Then
            FirstPlay = True
        End If

        Try
            MainForm.Show()
            Application.Run()
        Catch ex As Exception When Debugger.IsAttached = False
            MsgBox("Unhandled error" & vbCr & ex.Message & vbCr & ex.StackTrace)
        End Try
    End Sub
End Module

Public Class MainForm
    Implements Server.IServerCallback

    Friend GameID As Integer
    Friend CurrentGameName As String
    Dim CurrentQuestion As MPQService.Question
    Dim CurrentControl As Control
    Friend UserID As Integer

    Private Sub MainForm_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        Try
            If MPQClient.Server.State = ServiceModel.CommunicationState.Opened Then
                SetStatus("Please wait while client is closed")
                Dim CloseThread As New Threading.Thread(AddressOf CloseSub)
                CloseThread.Name = "CloseMPQClient"
                CloseThread.Start()
                e.Cancel = True
            Else
                Application.Exit()
            End If
        Catch ex As Exception
            Application.Exit()
        End Try
    End Sub

    Sub CloseSub()
        If Not GameID = 0 Then
            Try
                If Not UserID = 0 Then
                    MPQClient.Server.UnregisterUser(GameID, UserID)
                Else
                    MPQClient.Server.UnregisterClient(GameID)
                End If
            Catch
            End Try
        End If
retry:  Try
            MPQClient.Server.Goodbye()
            MPQClient.Server.Close()
        Catch ex As Exception
        End Try
        Application.Exit()
    End Sub

    Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        MainPanel.Controls.Add(New SelectServerControl With {.Location = New Point(0, MainPanel.Height / 2 - 150)})
        ScoreboardPanel.Controls.Add(New SplashControl With {.Name = "SplashControl"})
        ScoreboardPanel.Controls("SplashControl").BringToFront()
    End Sub

    Sub Connect()
        Dim ConnectThread As New Threading.Thread(AddressOf _Connect)
        ConnectThread.Name = "ConnectThread"
        ConnectThread.Start()
    End Sub

    Sub _Connect()
        Try
            If CheckForUpdate() Then Exit Sub
        Catch ex As Exception
            MsgBox("Failed to check for updates - client may not work." & vbCr & ex.Message)
        End Try
        Try
            MPQClient.Server = New Server.ServerClient(New System.ServiceModel.InstanceContext(Me))
            MPQClient.Server.Endpoint.Behaviors.Add(New MPQEndpointBehavior)
            MPQClient.Server.Endpoint.Address = MPQClient.EndpointAdr
            Invoke(New Action(AddressOf MPQClient.Server.Open))
            MPQClient.Server.Hello()
            Invoke(New Action(AddressOf ConnectSuccess))
        Catch ex As Exception
            Invoke(New Action(Of Exception)(AddressOf ConnectFail), ex)
        End Try
    End Sub

    Function CheckForUpdate() As Boolean
        Dim UpdateEndpoint As New ServiceModel.EndpointAddress(MPQClient.EndpointAdr.Uri.AbsoluteUri & "Update/")
        Dim UpdateServer As New UpdateServer.UpdateServerClient("NetTcpBinding_IUpdateServer")
        UpdateServer.Endpoint.Address = UpdateEndpoint
        UpdateServer.Open()
        If UpdateServer.NeedUpdate(My.Application.Info.Version, 4.0) Then
            Invoke(New Action(AddressOf ShowUpdatePanel))

            Dim UpdateProgressBar As New Action(Of Double)(AddressOf _UpdateProgressBar)

            Dim Files As List(Of String) = UpdateServer.GetFiles(4.0).ToList
            IO.Directory.CreateDirectory("Update\")
            For Each File As String In Files
                Dim Length As Long = UpdateServer.GetFileLength(4.0, Files.IndexOf(File))
                Dim GetStream As IO.Stream = UpdateServer.GetFile(4.0, Files.IndexOf(File))

                Dim FileStream As New IO.FileStream("Update\" & GetFileName(File), IO.FileMode.CreateNew)
                Dim chunkSize As Integer = 2048
                Dim buffer As Byte() = New Byte(chunkSize - 1) {}

                Do
                    Dim bytesRead As Integer = GetStream.Read(buffer, 0, chunkSize)
                    If bytesRead = 0 Then
                        Exit Do
                    End If

                    FileStream.Write(buffer, 0, bytesRead)

                    Invoke(UpdateProgressBar, (FileStream.Position / Length) * 100)
                Loop
                FileStream.Close()
            Next

            Dim ServerName As String
            With MPQClient.EndpointAdr.Uri
                If .AbsoluteUri.EndsWith("/Server/") Then
                    ServerName = .AbsoluteUri
                Else
                    ServerName = .AbsoluteUri.Remove(0, .AbsoluteUri.Length - 1)
                    ServerName = ServerName.Remove(0, ServerName.LastIndexOf("/") + 1)
                    ServerName = ServerName.Replace("_", " ")
                End If
            End With

            Shell("Cmd.exe /c " & """"c & "echo off & echo Welcome to MPQ Updater & pause & del MPQ* /q & xcopy Update\* /y /c & rmdir /s /q Update & echo Update Complete & start /b MPQClient.exe /s " & ServerName & " & pause" & """"c, AppWinStyle.NormalFocus)
            Application.Exit()
            Return True
        Else : Return False
        End If
    End Function

    Public Function GetFileName(ByVal FileName As String) As String
        Dim Name As String = ""

        Dim i As Integer = FileName.Length - 1

        Do Until FileName(i) = "/" Or FileName(i) = "\"
            Name = Name.Insert(0, FileName(i))
            i -= 1
        Loop

        Return Name
    End Function

    Sub ShowUpdatePanel()
        MainPanel.Controls.Clear()
        MainPanel.Controls.Add(New UpdateControl)
    End Sub

    Sub _UpdateProgressBar(ByVal Value As Double)
        CType(MainPanel.Controls(0), UpdateControl).ProgressBar.Value = Value
    End Sub

    Sub ConnectSuccess()
        MainPanel.Controls.Clear()
    End Sub

    Sub ConnectFail(ByVal ex As Exception)
        Try
            MsgBox("Could not connect." & vbCr & "Exception Details:" & vbCr & ex.Message & vbCr & ex.InnerException.Message)
        Catch
            MsgBox("Could not connect." & vbCr & "Exception Details:" & vbCr & ex.Message)
        End Try
        CType(MainPanel.Controls(0), SelectServerControl).OK_Button.Enabled = True
        CType(MainPanel.Controls(0), SelectServerControl).RefreshButton.Enabled = True
        CType(MainPanel.Controls(0), SelectServerControl).Servers.Enabled = True
    End Sub

    Public Sub OnHello(ByVal SingleGame As Boolean, ByVal ServerMessages() As MPQService.ServerMessage, ByVal ServerRequiresPassword As Boolean) Implements Server.IServerCallback.OnHello
        'KnowNeedPassword = True

        If ServerRequiresPassword Then
            ServerNeedPassword = True

            Dim sha1 As System.Security.Cryptography.SHA1 = System.Security.Cryptography.SHA1.Create()
            Dim hashData As Byte() = sha1.ComputeHash(System.Text.Encoding.[Default].GetBytes(InputBox("Please enter server password")))

            ServerPassword = Convert.ToBase64String(hashData)
        End If

        Dim Params() = {SingleGame, ServerMessages}

        Dim Thread As New Threading.Thread(AddressOf _OnHello)
        Thread.Name = "OnHello Thread"
        Thread.Start(Params)
    End Sub

    Sub _OnHello(ByVal Params())
        Dim SingleGame As String = Params(0)
        Dim ServerMessages() As MPQService.ServerMessage = Params(1)

        SingleGameOnly = SingleGame
        If SingleGameOnly Then FirstPlay = False
        Dim LoginControl As New LoginControl With {.Location = New Point(0, MainPanel.Height / 2 - 150)}
        If SingleGame Then
            GameID = 1
            With My.Application.CommandLineArgs
                If .Contains("/u") Then
                    Username = .Item(.IndexOf("/u") + 1)
                End If
            End With
        End If
        If ServerMessages.Count > 0 Then
            Dim SMC As New ServerMessagesControl(ServerMessages)
            SMC.Location = New Point(0, MainPanel.Height / 2 - 150)
            Invoke(New Action(Of Control)(AddressOf MainPanel.Controls.Add), SMC)
            SMC.WaitForContinue()
            Try
                Invoke(New Action(AddressOf SMC.Dispose))
            Catch
            End Try
        End If
        Try
            Invoke(New Action(Of Control)(AddressOf MainPanel.Controls.Add), LoginControl)
        Catch
        End Try
    End Sub

    Public Sub OnCorrectAnswer(ByVal Explanation As String) Implements Server.IServerCallback.OnCorrectAnswer
        MainPanel.Controls.Add(New CorrectAnswer(Explanation) With {.Location = New Point(0, MainPanel.Height / 2 - 150)})
    End Sub

    Public Sub OnIncorrectAnswer(ByVal CorrectAnswer As String, ByVal Explanation As String) Implements Server.IServerCallback.OnIncorrectAnswer
        MainPanel.Controls.Add(New IncorrectAnswer(CorrectAnswer, Explanation) With {.Location = New Point(0, MainPanel.Height / 2 - 150)})
    End Sub

    Public Sub OnRegisterUser(ByVal ID As Integer) Implements Server.IServerCallback.OnRegisterUser
        UserID = ID
        CType(MainPanel.Controls(0), WelcomeControl).StartButton.Text = "Waiting for host to start game"
    End Sub

    Public Sub OnGetGameDetails(ByVal Details As MPQService.GameDetails) Implements Server.IServerCallback.OnGetGameDetails
        Try
            With CType(MainPanel.Controls(0), WelcomeControl)
                .QuizNameLabel.Text = Details.Name
                .QuizNameLabel.Visible = True
                .QuizDescriptionLabel.Text = Details.Description
                .QuizDescriptionLabel.Visible = True
                .AuthorLabel.Text = Details.Author
                .AuthorLabel.Visible = True
                .TotalQuestionsLabel.Text = "Total Questions: " & Details.TotalQuestions
                .TotalQuestionsLabel.Visible = True

                If Not Details.Image = "" Then
                    Dim MS As New IO.MemoryStream(Convert.FromBase64String(Details.Image))
                    .QuizImage.Image = Image.FromStream(MS)
                    .QuizImage.Visible = True
                    .GameNameLabel.Location = New Point(109, 20)
                    .GameNameLabel.Width = 388
                    .QuizNameLabel.Location = New Point(109, 43)
                    .QuizNameLabel.Width = 388
                    .QuizDescriptionLabel.Location = New Point(159, 76)
                    .QuizDescriptionLabel.Width = 388
                End If
            End With
        Catch ex As Exception
        End Try
    End Sub

    Public Sub OnGetQuizFiles(ByVal Files() As String) Implements Server.IServerCallback.OnGetQuizFiles
        Dim SelectedItem As String = ""
        With CType(LeftPanel.Controls("SelectQuizControl"), SelectQuizControl)
            If Not .ListBox1.SelectedIndex = -1 Then SelectedItem = .ListBox1.SelectedItem
            .ListBox1.Items.Clear()
            For Each file As String In Files
                .ListBox1.Items.Add(file.Remove(0, 11))
            Next
            If Not SelectedItem = "" Then .ListBox1.SelectedItem = SelectedItem
        End With
    End Sub

    Public Sub SendQuestion(ByVal Question As MPQService.Question) Implements Server.IServerCallback.SendQuestion
        Try
            CurrentQuestion = Question
            If CurrentQuestion.IsMultichoice = True Then
                CurrentControl = New MultichoiceControl With {.Location = New Point(0, MainPanel.Height / 2 - 150)}
                CType(CurrentControl, MultichoiceControl).NewQuestion(CurrentQuestion)
                AddHandler CType(CurrentControl, MultichoiceControl).AnswerGiven, AddressOf MultichoiceAnswer
                AddHandler CType(CurrentControl, MultichoiceControl).OutOfTime, AddressOf OutOfTime
            Else
                CurrentControl = New InputControl With {.Location = New Point(0, MainPanel.Height / 2 - 150)}
                CType(CurrentControl, InputControl).NewQuestion(CurrentQuestion)
                AddHandler CType(CurrentControl, InputControl).AnswerGiven, AddressOf InputAnswer
                AddHandler CType(CurrentControl, InputControl).OutOfTime, AddressOf OutOfTime
            End If
            CurrentControl.Visible = False
            If Not CurrentQuestion.Image Is Nothing Then
                Dim MS As New IO.MemoryStream(Convert.FromBase64String(CurrentQuestion.Image))
                QuestionImage.Image = Image.FromStream(MS)
            End If
            MainPanel.Controls.Add(CurrentControl)
            MPQClient.Server.ReadyToStart(GameID, UserID)
        Catch ex As Exception
            SetStatus(ex.Message)
        End Try
    End Sub

    Public Sub Start() Implements Server.IServerCallback.Start
        Dim Controls(MainPanel.Controls.Count - 1) As Control
        MainPanel.Controls.CopyTo(Controls, 0)
        For Each Control As Control In Controls
            If Not Control Is CurrentControl Then
                Control.Dispose()
                MainPanel.Controls.Remove(Control)
            End If
        Next
        If Not CurrentQuestion.Image Is Nothing Then
            QuestionImage.Visible = True
            LeftPanel.Text = "Question Image"
        End If
        CurrentControl.Visible = True
        CType(CurrentControl, Object).Start()

        'NB: Test Lines
        If My.Application.CommandLineArgs.Contains("/DEBUG") Then
            Threading.Thread.Sleep((New Random).Next(1, 10) * 1000)
            If CurrentQuestion.IsMultichoice = True Then
                Select Case (New Random).Next(1, 4)
                    Case 1
                        CType(CurrentControl, MultichoiceControl).AnswerA.PerformClick()
                    Case 2
                        CType(CurrentControl, MultichoiceControl).AnswerB.PerformClick()
                    Case 3
                        CType(CurrentControl, MultichoiceControl).AnswerC.PerformClick()
                    Case 4
                        CType(CurrentControl, MultichoiceControl).AnswerD.PerformClick()
                End Select
            Else
                CType(CurrentControl, InputControl).Submit.PerformClick()
            End If
        End If
    End Sub

    Private Sub MultichoiceAnswer(ByVal Answer As MPQService.Answer)
        If QuestionImage.Visible = True Then
            QuestionImage.Visible = False
            LeftPanel.Text = "Console"
        End If
        MPQClient.Server.MultichoiceAnswer(GameID, Answer, UserID)
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
    End Sub

    Private Sub InputAnswer(ByVal Answer As String)
        If QuestionImage.Visible = True Then
            QuestionImage.Visible = False
            LeftPanel.Text = "Console"
        End If
        MPQClient.Server.InputAnswer(GameID, Answer, UserID)
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
    End Sub

    Private Sub OutOfTime()
        If QuestionImage.Visible = True Then
            QuestionImage.Visible = False
            LeftPanel.Text = "Console"
        End If
        MPQClient.Server.OutOfTime(GameID, UserID)
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
    End Sub

    Public Sub UpdateGameScores(ByVal Scores As Dictionary(Of String, Long)) Implements Server.IServerCallback.UpdateGameScores
        Try
            For Each User As String In Scores.Keys
                For Each Item As ListViewItem In Scoreboard.Items
                    If Item.Text = User Then
                        Item.SubItems.Item(1).Text = Scores(User)
                        GoTo nextuser
                    End If
                Next
                Scoreboard.Items.Add(User).SubItems.Add(Scores(User))
nextuser:
            Next
            Scoreboard.ListViewItemSorter = New ScoresComparer
            Scoreboard.Sort()
        Catch ex As Exception
        End Try
    End Sub

    Public Sub HaveHost(ByVal HaveHost As Boolean) Implements Server.IServerCallback.HaveHost
        Try
            If HaveHost = True Then
                CType(MainPanel.Controls(0), WelcomeControl).HostButton.Visible = False
            Else
                CType(MainPanel.Controls(0), WelcomeControl).HostButton.Visible = True
            End If
        Catch
        End Try
    End Sub

    Public Sub OnRegisterHost(ByVal ID As Integer) Implements Server.IServerCallback.OnRegisterHost
        UserID = ID
        CType(MainPanel.Controls(0), WelcomeControl).StartButton.Text = "Start Quiz"
        CType(MainPanel.Controls(0), WelcomeControl).StartButton.Enabled = True
    End Sub

    Public Sub EndOfQuiz(ByVal UserScore As Integer, ByVal TotalQuestions As Integer) Implements Server.IServerCallback.EndOfQuiz
        UserID = 0
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
        MainPanel.Controls.Add(New EndControl(UserScore, TotalQuestions) With {.Location = New Point(0, MainPanel.Height / 2 - 150)})
    End Sub

    Public Sub PlayAgain()
        Scoreboard.Items.Clear()
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
        MainPanel.Controls.Add(New WelcomeControl With {.Location = New Point(0, MainPanel.Height / 2 - 150), .GameName = CurrentGameName})
        MPQClient.Server.GetHaveHost(GameID)
        'Text = "Multiplayer Quiz Client"
    End Sub

    Public Sub ReturnToLobby()
        LeftPanel.Text = "Server Console"
        MPQClient.Server.UnregisterClient(GameID)
        UserID = 0
        GameID = 0
        CurrentGameName = ""
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
        MainPanel.Controls.Add(New HomeControl With {.Location = New Point(0, MainPanel.Height / 2 - 150)})
    End Sub

    Public Sub ServerSetStatus(ByVal Status As String, ByVal Detail As String) Implements Server.IServerCallback.SendStatus
        SetStatus(Status, Detail)
    End Sub

    Dim ConsoleLines As New List(Of String)
    Dim LastDetailStatus As String
    Dim LastDetail As String
    Public Sub SetStatus(ByVal Status As String, Optional ByVal Detail As String = "")
        If LastDetailStatus = Status Then
            With ConsoleLines
                .Item(.LastIndexOf(Status & " (" & LastDetail & ")")) = Status & " (" & Detail & ")"
            End With
        Else
            If Detail = "" Then
                ConsoleLines.Add(Status)
            Else
                ConsoleLines.Add(Status & " (" & Detail & ")")
            End If
        End If
        If ConsoleLines.Count = 51 Then
            ConsoleLines.RemoveAt(0)
        End If

        SetConsoleLines()
        
        If Not Detail = "" Then
            LastDetailStatus = Status
            LastDetail = Detail
        End If
    End Sub

    Public Sub SetConsoleLines()
        If InvokeRequired Then
            Try
                Invoke(New Action(AddressOf SetConsoleLines))
            Catch
            End Try
            Exit Sub
        End If
        Console.Lines = ConsoleLines.ToArray
        Console.SelectionStart = Console.Text.Length
        Console.ScrollToCaret()
    End Sub

    Private Sub MainForm_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
        Try
            MainPanel.Controls(0).Location = New Point(0, MainPanel.Height / 2 - 150)
        Catch
        End Try
    End Sub

    Public Sub OnGetGames(ByVal Games As System.Collections.Generic.Dictionary(Of String, Integer)) Implements Server.IServerCallback.OnGetGames
        CType(MainPanel.Controls(0), HomeControl).Dict = Games
        CType(MainPanel.Controls(0), HomeControl).GameList.Items.Clear()
        For Each Name As String In CType(MainPanel.Controls(0), HomeControl).Dict.Keys
            CType(MainPanel.Controls(0), HomeControl).GameList.Items.Add(Name)
        Next
        CType(MainPanel.Controls(0), HomeControl).EnableButtons()

        'NB: Test Lines
        With My.Application.CommandLineArgs
            If .Contains("/DEBUG") Then
                Dim DebugJoinGameThread As New Threading.Thread(AddressOf DebugJoinGame)
                DebugJoinGameThread.Name = "DebugJoinGameThread"
                DebugJoinGameThread.Start(Games)
            End If
        End With
    End Sub

    Sub DebugJoinGame(ByVal Games As System.Collections.Generic.Dictionary(Of String, Integer))
        If InvokeRequired Then
            System.Threading.Thread.Sleep(10000)
            Invoke(New Action(Of System.Collections.Generic.Dictionary(Of String, Integer))(AddressOf DebugJoinGame), Games)
            Exit Sub
        End If

        With My.Application.CommandLineArgs
            If Games.Keys.Contains(.Item(.IndexOf("/DEBUG") + 1)) Then
                CType(MainPanel.Controls(0), HomeControl).GameList.SelectedItem = .Item(.IndexOf("/DEBUG") + 1)
                CType(MainPanel.Controls(0), HomeControl).JoinButton.PerformClick()
            Else
                CType(MainPanel.Controls(0), HomeControl).CreateButton.PerformClick()
            End If
        End With
    End Sub

    Public Sub OnFailRegisterClient(ByVal Reason As String) Implements Server.IServerCallback.OnFailRegisterClient
        GameID = 0
        CurrentGameName = ""
        MsgBox("Game no longer exists. Please create a new one or select another")
        CType(MainPanel.Controls(0), HomeControl).RefreshButton.Enabled = True
        CType(MainPanel.Controls(0), HomeControl).RefreshButton.PerformClick()
    End Sub

    Public Sub OnRegisterClient(ByVal GameName As String) Implements Server.IServerCallback.OnRegisterClient
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
        MainPanel.Controls.Add(New WelcomeControl With {.Location = New Point(0, MainPanel.Height / 2 - 150), .GameName = GameName})
        LeftPanel.Text = "Quiz Console"
        If SingleGameOnly Then
            CType(MainPanel.Controls(0), WelcomeControl).HostButton.Visible = False
            CType(MainPanel.Controls(0), WelcomeControl).StartButton.PerformClick()
        End If
    End Sub

    Public Sub OnIncorrectServerPassword() Implements Server.IServerCallback.OnIncorrectServerPassword
        MsgBox("You have entered an incorrect server password.")

        Dim sha1 As System.Security.Cryptography.SHA1 = System.Security.Cryptography.SHA1.Create()
        Dim hashData As Byte() = sha1.ComputeHash(System.Text.Encoding.[Default].GetBytes(InputBox("Please enter server password")))

        ServerPassword = Convert.ToBase64String(hashData)

        Try
            CType(MainPanel.Controls(0), LoginControl).LoginButton.Enabled = True
            CType(MainPanel.Controls(0), LoginControl).LoginButton.PerformClick()
        Catch
        End Try
    End Sub

    Public Sub OnLoginFail() Implements Server.IServerCallback.OnLoginFail
        CType(MainPanel.Controls(0), LoginControl).LoginButton.Enabled = True
        CType(MainPanel.Controls(0), LoginControl).CreateUserButton.Enabled = True
        CType(MainPanel.Controls(0), LoginControl).UsernameTextBox.Enabled = True
        CType(MainPanel.Controls(0), LoginControl).PasswordTextBox.Enabled = True

        With My.Application.CommandLineArgs
            If .Contains("/u") Then
                If .Contains("/p") Then
                    MPQClient.Server.CreateUser(.Item(.IndexOf("/u") + 1), .Item(.IndexOf("/p") + 1))
                Else
                    MPQClient.Server.CreateUser(.Item(.IndexOf("/u") + 1), "")
                End If
                Exit Sub
            End If
        End With
        MsgBox("Incorrect Login Details. Please Try Again")
    End Sub

    Public Sub OnLoginSuccess() Implements Server.IServerCallback.OnLoginSuccess
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
        MainPanel.Controls.Add(New HomeControl With {.Location = New Point(0, MainPanel.Height / 2 - 150)})
        SetStatus("Welcome " & MPQClient.Username)
        Text += " - " & MPQClient.Username
    End Sub

    Public Sub OnUpdateTeamScores(ByVal Scores As System.Collections.Generic.Dictionary(Of String, Long)) Implements Server.IServerCallback.OnUpdateTeamScores
        Try
            For Each Team As String In Scores.Keys
                For Each Item As ListViewItem In CType(MainPanel.Controls(0), HomeControl).TeamScoresView.Items
                    If Item.Text = Team Then
                        Item.SubItems.Item(1).Text = Scores(Team)
                        GoTo nextuser
                    End If
                Next
                CType(MainPanel.Controls(0), HomeControl).TeamScoresView.Items.Add(Team).SubItems.Add(Scores(Team))
nextuser:
            Next
            CType(MainPanel.Controls(0), HomeControl).TeamScoresView.ListViewItemSorter = New ScoresComparer
            CType(MainPanel.Controls(0), HomeControl).TeamScoresView.Sort()

            For Each Item As ListViewItem In CType(MainPanel.Controls(0), HomeControl).TeamScoresView.Items
                Try
                    Item.SubItems.Item(2).Text = Item.Index + 1
                Catch ex As Exception
                    Item.SubItems.Add(Item.Index + 1)
                End Try
            Next
        Catch ex As Exception
            SetStatus("Error updating team scores: " & ex.Message)
        End Try
    End Sub

    Public Sub OnUpdateUserScores(ByVal Scores As System.Collections.Generic.Dictionary(Of String, Long)) Implements Server.IServerCallback.OnUpdateUserScores
        Try
            For Each User As String In Scores.Keys
                For Each Item As ListViewItem In CType(MainPanel.Controls(0), HomeControl).UserScoresView.Items
                    If Item.Text = User Then
                        Item.SubItems.Item(1).Text = Scores(User)
                        GoTo nextuser
                    End If
                Next
                CType(MainPanel.Controls(0), HomeControl).UserScoresView.Items.Add(User).SubItems.Add(Scores(User))
nextuser:
            Next
            CType(MainPanel.Controls(0), HomeControl).UserScoresView.ListViewItemSorter = New ScoresComparer
            CType(MainPanel.Controls(0), HomeControl).UserScoresView.Sort()

            For Each Item As ListViewItem In CType(MainPanel.Controls(0), HomeControl).UserScoresView.Items
                Try
                    Item.SubItems.Item(2).Text = Item.Index + 1
                Catch ex As Exception
                    Item.SubItems.Add(Item.Index + 1)
                End Try
            Next
        Catch ex As Exception
            SetStatus("Failed to update user scores: " & ex.Message)
        End Try
    End Sub

    Public Sub OnCreateUserFail(ByVal Reason As String) Implements Server.IServerCallback.OnCreateUserFail
        MsgBox("Could not create user." & vbCr & "Please ensure that you have entered a username and password." & vbCr & Reason)
    End Sub

    Public Sub OnFailUpload(ByVal Exception As System.Exception) Implements Server.IServerCallback.OnFailUpload
        If Exception.Message = "Other Quiz Being Uploaded, Please Wait" Then
            MsgBox("Server busy, please wait and try again.")
            CType(LeftPanel.Controls("SelectQuizControl"), SelectQuizControl).ProgressBar.Hide()
        End If
    End Sub

    Public Sub OnChangePassword(ByVal Success As Boolean, ByVal Exception As System.Exception) Implements Server.IServerCallback.OnChangePassword
        CType(ScoreboardPanel.Controls("UserControl"), UserControl).OnPasswordChange(Success, Exception)
    End Sub

    Public Sub OnGetUserDetails(ByVal Score As Integer, ByVal TeamName As String, ByVal TimeMultiplier As Double) Implements Server.IServerCallback.OnGetUserDetails
        Try
            CType(ScoreboardPanel.Controls("UserControl"), UserControl).ScoreLabel.Text = Score
            CType(ScoreboardPanel.Controls("UserControl"), UserControl).TeamLabel.Text = TeamName
            CType(ScoreboardPanel.Controls("UserControl"), UserControl).TimeMultiplierLabel.Text = TimeMultiplier
        Catch ex As Exception
        End Try
    End Sub

    Private Sub LeaveGameButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LeaveGameButton.Click
        If Not UserID = 0 Then
            MPQClient.Server.UnregisterUser(GameID, UserID)
        Else
            MPQClient.Server.UnregisterClient(GameID)
        End If
        If QuestionImage.Visible = True Then
            QuestionImage.Visible = False
            LeftPanel.Text = "Server Console"
        End If
        UserID = 0
        GameID = 0
        CurrentGameName = ""
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
        MainPanel.Controls.Add(New HomeControl With {.Location = New Point(0, MainPanel.Height / 2 - 150)})
    End Sub

    Friend Sub DoLogOut()
        MPQClient.Server.Goodbye()
        MainPanel.Controls(0).Dispose()
        MainPanel.Controls.Clear()
        MainPanel.Controls.Add(New LoginControl With {.Location = New Point(0, MainPanel.Height / 2 - 150)})
        ScoreboardPanel.Controls.Add(New SplashControl With {.Name = "SplashControl"})
        ScoreboardPanel.Controls("SplashControl").BringToFront()
    End Sub
End Class

Class ScoresComparer
    Implements IComparer

    Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
        Try
            Dim returnVal As Integer = -1
            returnVal = 1 - Decimal.Compare(CType(x, ListViewItem).SubItems(1).Text, CType(y, ListViewItem).SubItems(1).Text)
            Return returnVal
        Catch
            Return 0
        End Try
    End Function
End Class

Class MPQEndpointBehavior
    Implements ServiceModel.Description.IEndpointBehavior
    Implements ServiceModel.Dispatcher.IClientMessageInspector

    Public Sub AddBindingParameters(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal bindingParameters As System.ServiceModel.Channels.BindingParameterCollection) Implements System.ServiceModel.Description.IEndpointBehavior.AddBindingParameters

    End Sub

    Public Sub ApplyClientBehavior(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal clientRuntime As System.ServiceModel.Dispatcher.ClientRuntime) Implements System.ServiceModel.Description.IEndpointBehavior.ApplyClientBehavior
        clientRuntime.MessageInspectors.Add(Me)
    End Sub

    Public Sub ApplyDispatchBehavior(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal endpointDispatcher As System.ServiceModel.Dispatcher.EndpointDispatcher) Implements System.ServiceModel.Description.IEndpointBehavior.ApplyDispatchBehavior
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(Me)
    End Sub

    Public Sub Validate(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint) Implements System.ServiceModel.Description.IEndpointBehavior.Validate

    End Sub

    Public Function BeforeSendRequest(ByRef request As System.ServiceModel.Channels.Message, ByVal channel As System.ServiceModel.IClientChannel) As Object Implements System.ServiceModel.Dispatcher.IClientMessageInspector.BeforeSendRequest
        'If channel.State = CommunicationState.Faulted Then
        '    request.Close()
        '    MsgBox("Apologies - the connection to the server is faulted." & vbCr & "Please restart MPQ Client." & vbCr & "This may have happened because you entered an incorrect password.")
        '    Application.Exit()
        '    Return Nothing
        '    Exit Function
        'End If
        'request.Headers.Add(New MessageHeader(Of Boolean)(KnowNeedPassword, False, "", False).GetUntypedHeader("KnowNeedPassword", "ns"))
        request.Headers.Add(New MessageHeader(Of String)() With {.Content = ServerPassword, .Actor = ""}.GetUntypedHeader("ServerPassword", "ns"))

        Return Nothing
    End Function

    Public Sub AfterReceiveReply(ByRef reply As System.ServiceModel.Channels.Message, ByVal correlationState As Object) Implements System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply

    End Sub
End Class